package miggy.dma;

import miggy.SystemModel;
import miggy.api.config.MachineDefinition;
import miggy.api.cpu.Size;
import miggy.api.memory.CustomRegisterObserver;
import miggy.api.dma.DMAController;
import miggy.api.dma.DMAChannel;
import miggy.memory.CustomRegisters;

import java.util.logging.Logger;

/*
//  Miggy - Java Amiga MachineCore
//  Copyright (c) 2008, Tony Headford
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
//  following conditions are met:
//
//    o  Redistributions of source code must retain the above copyright notice, this list of conditions and the
//       following disclaimer.
//    o  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
//       following disclaimer in the documentation and/or other materials provided with the distribution.
//    o  Neither the name of the Miggy Project nor the names of its contributors may be used to endorse or promote
//       products derived from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// $Revision: 21 $
*/

public class DmaManager implements DMAController
{
	private static Logger logger = Logger.getLogger("Miggy.DMA");

	private BlitterChannel blitter;
	private DMAChannel bitplane;
	private DMAChannel copper;
	private AudioChannel audio;
	private SpriteChannel sprites;
	private DMAChannel disk;
	private boolean masterEnable;
	private int dmacon;

	public DmaManager()
	{
	}

	public void init(MachineDefinition config)
	{
		blitter = new BlitterChannel();
		bitplane = new BitplaneChannel();
		copper = new CopperChannel();
		audio = new AudioChannel();
		sprites = new SpriteChannel();
		disk = new DiskChannel();
		masterEnable = false;

		//we watch for changes to DMACON
		SystemModel.REGS.addObserver(CustomRegisters.DMACON, new CustomRegisterObserver() {
			public void update(int register, int value, Size size)
			{
				updateDMAControl(value);
			}}
		);
	}

	public void reset()
	{
		masterEnable = false;
		disk.reset();
		audio.reset();
		sprites.reset();
		bitplane.reset();
		copper.reset();
		blitter.reset();
	}

	public final int execute(int scanline)
	{
		if(!masterEnable)
			return 0;

		//25 DMA channels - we use 24 - 1 is for memory refresh
		int cycles = 0;

		//1 disk channel
		cycles += disk.doWork(scanline);

		//4 audio channels
		cycles += audio.doWork(scanline);

		//in a real amiga the sprite DMA slots are before the bitplane DMA slots
		//but handling it afterwards will allow updating of display with sprites on top
		//6 bitplane channels
		cycles += bitplane.doWork(scanline);

		//8 sprites channels
		//the amount of slots available here depend on the display size
		cycles += sprites.doWork(scanline);

		//in a real amiga copper has higher priority than the blitter but this will allow the
		//copper to update the blitted pixels in the buffer (ie colour changes) -is this right ?
		//4 blitter channels
		cycles += blitter.doWork(scanline);

		//1 Copper channel
		cycles += copper.doWork(scanline);

		//return the number of cycles left for the cpu to use this scanline
		return cycles;
	}

	private void updateDMAControl(int value)
	{
		//are we enabling or disabling
		boolean enable = (value & 0x8000) != 0;

		//work out what is being changed
		if((value & 0x0400) != 0)
		{
			blitter.setNasty(enable);
		}

		if((value & 0x0200) != 0)
		{
			masterEnable = enable;
		}

		if((value & 0x0100) != 0)
		{
			bitplane.enable(enable);
		}
		if((value & 0x0080) != 0)
		{
			copper.enable(enable);
		}
		if((value & 0x0040) != 0)
		{
			blitter.enable(enable);
		}
		if((value & 0x0020) != 0)
		{
			sprites.enable(enable);
		}
		if((value & 0x0010) != 0)
		{
			disk.enable(enable);
		}
		if((value & 0x0008) != 0)
		{
			audio.enableChannel(3, enable);
		}
		if((value & 0x0004) != 0)
		{
			audio.enableChannel(2, enable);
		}
		if((value & 0x0002) != 0)
		{
			audio.enableChannel(1, enable);
		}
		if((value & 0x0001) != 0)
		{
			audio.enableChannel(0, enable);
		}
	}
}
